package cc.aiee.wxpay.api;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLSocketFactory;

import cc.aiee.core0.bean.J;
import cc.aiee.core0.bean.R;
import cc.aiee.core0.enums.MsgCodeEnum;
import cc.aiee.core0.utils.ValidUtil;
import cc.aiee.wxpay.config.WxPayConfig;
import cc.aiee.wxpay.config.WxPayConfigKit;
import cc.aiee.wxpay.core.constants.WxPayConst;
import cc.aiee.wxpay.core.enums.WxPayUrlEnum;
import cc.aiee.wxpay.core.pojo.dto.WxPayAppPaymentDTO;
import cc.aiee.wxpay.core.pojo.dto.WxPayPaymentMinAppDTO;
import cc.aiee.wxpay.core.pojo.dto.WxPayRefundDTO;
import cc.aiee.wxpay.core.pojo.dto.WxPayUnifiedorderDTO;
import cc.aiee.wxpay.core.pojo.query.WxPayQuery;
import cc.aiee.wxpay.core.tool.WxPayTool;
import cc.aiee.wxpay.model.WxPayRefundModel;
import cc.aiee.wxpay.model.WxPayUnifiedorderModel;
import cc.aiee.wxpay.model.minapp.WxPayUnifiedorderMinappModel;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WxPayApi {

	/**
	 * App 统一支付
	 * 
	 * @param model
	 * @author YangLong <br>
	 *         2020年12月18日
	 */
	public J<WxPayQuery, WxPayUnifiedorderDTO> unifiedorder(WxPayUnifiedorderModel model) {
//		ValidUtil.asserts(model);
//		Map<String, Object> beanToMap = BeanUtil.beanToMap(model, false, true);
//
//		WxPayConfig config = WxPayConfigKit.getDefaultConfig();
//		supplement(beanToMap, config);

//		beanToMap.put("openid", "oyDIG5pFGHQpsxllSCyFzoN8bEfQ");

		WxPayConfig config = WxPayConfigKit.getDefaultConfig();
		Map<String, Object> beanToMap = beanToMap(model, config);
		supplementOfNotifyUrl(beanToMap, config, null);

		// 签名
		String sign = WxPayTool.getSign(beanToMap, config.getApiSecret());
		beanToMap.put(WxPayConst.SIGN, sign);

		String mapToXmlStr = XmlUtil.mapToXmlStr(beanToMap);
		String url = WxPayConfigKit.getURL(WxPayUrlEnum.UNIFIEDORDER);
		String post = HttpUtil.post(url, mapToXmlStr);
		if (log.isDebugEnabled()) {
			log.debug("WxPay 请求地址:{} \n param:{} \nresult:{}", url, mapToXmlStr, post);
		}
		WxPayQuery query = new WxPayQuery(url, mapToXmlStr, post, sign);
		WxPayUnifiedorderDTO xmlToBean = WxPayTool.xmlToBean(post, WxPayUnifiedorderDTO.class);
		return new J<>(WxPayTool.ok(xmlToBean), xmlToBean, query);
	}

	public J<WxPayQuery, WxPayUnifiedorderDTO> unifiedorderMinApp(WxPayUnifiedorderMinappModel model) {
		WxPayConfig config = WxPayConfigKit.getDefaultConfig();
		Map<String, Object> beanToMap = beanToMap(model, config);
		supplementOfNotifyUrl(beanToMap, config, null);
		// 签名
		String sign = WxPayTool.getSign(beanToMap, config.getApiSecret());
		beanToMap.put(WxPayConst.SIGN, sign);
		
		String mapToXmlStr = XmlUtil.mapToXmlStr(beanToMap);
		String url = WxPayConfigKit.getURL(WxPayUrlEnum.UNIFIEDORDER);
		String post = HttpUtil.post(url, mapToXmlStr);
		if (log.isDebugEnabled()) {
			log.debug("WxPay 请求地址:{} \n param:{} \nresult:{}", url, mapToXmlStr, post);
		}
		WxPayQuery query = new WxPayQuery(url, mapToXmlStr, post, sign);
		WxPayUnifiedorderDTO xmlToBean = WxPayTool.xmlToBean(post, WxPayUnifiedorderDTO.class);
		return new J<>(WxPayTool.ok(xmlToBean), xmlToBean, query);
	}

	/**
	 * 二次签名,小程序支付
	 * @param dto
	 * @return
	 */
	public R<WxPayPaymentMinAppDTO> paymentMinApp(WxPayUnifiedorderDTO dto){
		String appid = dto.getAppid();
		String timestamp = String.valueOf(DateUtil.currentSeconds());
		String noncestr = dto.getNonce_str();
		String package2 = "prepay_id="+dto.getPrepay_id();
		String signType = "MD5";

		Map<String, Object> map = new HashMap<>();
		map.put("appId", appid);
		map.put("timeStamp", timestamp);
		map.put("nonceStr", noncestr);
		map.put("package", package2);
		map.put("signType", signType);

		WxPayConfig config = WxPayConfigKit.getDefaultConfig();
		String sign = WxPayTool.getSign(map, config.getApiSecret());

		WxPayPaymentMinAppDTO pay = new WxPayPaymentMinAppDTO();
		pay.setAppId(appid);
		pay.setTimeStamp(timestamp);
		pay.setNonceStr(noncestr);
		pay.setPackage2(package2);
		pay.setSignType(signType);
		pay.setPaySign(sign);
		return new R<>(MsgCodeEnum.SUCCESS, pay);
	}
	
	/**
	 * 二次签名，APP支付
	 * 
	 * @param dto
	 * @return
	 */
	public R<WxPayAppPaymentDTO> paymentApp(WxPayUnifiedorderDTO dto) {
		String appid = dto.getAppid();
		String partnerid = dto.getMch_id();
		String prepayid = dto.getPrepay_id();
		String package2 = "Sign=WXPay";
		String noncestr = dto.getNonce_str();
		String timestamp = String.valueOf(DateUtil.currentSeconds());

		Map<String, Object> map = new HashMap<>();
		map.put("appid", appid);
		map.put("partnerid", partnerid);
		map.put("prepayid", prepayid);
		map.put("package", package2);
		map.put("noncestr", noncestr);
		map.put("timestamp", timestamp);

		WxPayConfig config = WxPayConfigKit.getDefaultConfig();
		String sign = WxPayTool.getSign(map, config.getApiSecret());

		WxPayAppPaymentDTO pay = new WxPayAppPaymentDTO();

		pay.setAppid(appid);
		pay.setPartnerid(partnerid);
		pay.setPrepayid(prepayid);
		pay.setPackage2(package2);
		pay.setNoncestr(noncestr);
		pay.setTimestamp(timestamp);
		pay.setSign(sign);

		return new R<>(MsgCodeEnum.SUCCESS, pay);
	}

	/**
	 * 数据补充
	 * 
	 * @param beanToMap
	 * @param config
	 * @author YangLong <br>
	 *         2020年12月18日
	 */
	public void supplement(Map<String, Object> beanToMap, WxPayConfig config) {
		if (false == beanToMap.containsKey(WxPayConst.APPID)) {
			beanToMap.put(WxPayConst.APPID, config.getAppId());
		}
		beanToMap.put(WxPayConst.MCH_ID, config.getMchId());
	}

	public void supplementOfNotifyUrl(Map<String, Object> beanToMap, WxPayConfig config, String notifyUrl) {
		if (false == beanToMap.containsKey(WxPayConst.NOTIFY_URL)
				|| StrUtil.isBlankIfStr(beanToMap.get(WxPayConst.NOTIFY_URL))) {
			if (StrUtil.isNotBlank(notifyUrl)) {
				beanToMap.put(WxPayConst.NOTIFY_URL, notifyUrl);
				return;
			}
			beanToMap.put(WxPayConst.NOTIFY_URL, config.getNotifyUrl());
		}
	}

	public <T> Map<String, Object> beanToMap(T t, WxPayConfig config) {
		ValidUtil.asserts(t);
		Map<String, Object> beanToMap = beanToMap(t);
		supplement(beanToMap, config);
		return beanToMap;
	}

	public <T> Map<String, Object> beanToMap(T t) {
//		ValidUtil.asserts(t);
		Map<String, Object> beanToMap = BeanUtil.beanToMap(t, false, true);
		return beanToMap;
	}

	public J<WxPayQuery, WxPayRefundDTO> refund(WxPayRefundModel refundModel) {
		WxPayQuery query = new WxPayQuery();
		try {
			WxPayConfig config = WxPayConfigKit.getDefaultConfig();
			Map<String, Object> beanToMap = beanToMap(refundModel, config);
			if (StrUtil.isNotBlank(config.getNotifyUrlRefund())) {
				supplementOfNotifyUrl(beanToMap, config, config.getNotifyUrlRefund());
			}
			WxPayTool.sign(beanToMap, config.getApiSecret());

			String mapToXmlStr = XmlUtil.mapToXmlStr(beanToMap);
			String url = WxPayConfigKit.getURL(WxPayUrlEnum.REFUND);

			query.setParam(mapToXmlStr);
			query.setUrl(url);

			SSLSocketFactory SSLBuild = SSLSocketFactoryBuilder.create().setProtocol(SSLSocketFactoryBuilder.TLSv1)
					.setKeyManagers(WxPayTool.getKeyManager(config.getCertPwd(), config.getCertPath(), null))
					.setSecureRandom(new SecureRandom()).build();

			String body = HttpUtil.createPost(url).setSSLSocketFactory(SSLBuild).body(mapToXmlStr).execute().body();

			query.setResult(body);

			WxPayRefundDTO xmlToBean = WxPayTool.xmlToBean(body, WxPayRefundDTO.class);

			return new J<>(WxPayTool.ok(xmlToBean), xmlToBean, query);
		} catch (KeyManagementException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new J<>(MsgCodeEnum.FAIL, null, query);
	}
}
